/**   
* The class InfixCalculator calculates a given expression in infix format   
* @author Michael Fildstein, ID: 309161594        
* @author Tomer Ben Rachel ID: 021982475        
*/  


public class InfixCalculator {
	
	
	private double result = -1;
	
   PeekableStackAsArray stack;
	

/**
 * The getter method getResult returns the result of the expression
 * @return A double data type representing the result
*/
	public double getResult(){
		return result;
	}


/**   
* The method evaluate receives an expression in string form, and calculates the value of that expression in these steps:   
* It turns the string into a series of tokens and while there are still tokens inside the string it:   
* Checks to see if the first 3 objects in the stack are in the format of two ValueTokens that have an arethmetic operation
* between them and then it pops them all out and applies the arethmetic operation on the two numbers and pushes the result
* back into the stack 
* Checks to see if the first 3 objects in the stack are in the format of an OpenBracket token, a ValueToken and a CloseBracket
* token and then it pushes them all out and pushes back only the ValueToken
* @param expr A string data type representing the infix expression   
* @return A double data type representing the value of the infix expression   
*/  

	public boolean evaluate(String expr){
		boolean ans = true;
		Object temp;
		ExpTokenizer token = new ExpTokenizer(expr);
		int stackSize = token.countTokens();
		this.stack = new PeekableStackAsArray(stackSize);
		
		while (token.hasMoreElements() && ans ){
         temp = token.nextElement();
			   if (!(temp instanceof ErrorToken)){
               this.collapse((CalcToken)temp);
               this.stack.push(temp);
         }else{
               ans = false;
               this.result = -1;
              }
			
		    }  
		temp = new AddOp();
		this.collapse((CalcToken)temp);
		if (this.stack.size() == 0 && this.stack.peek(0) instanceof ValueToken){   //If there is only one object left it is the result of the infix expression
         ans = true;
         this.result = ((ValueToken)this.stack.pop()).getValue();
		}else{
         ans = false;
			   result = -1;
		    }
				
		return ans;
		
	}
	
	
/**   
* The method collapse receives a token of CalcToken type and peeks at the top 3 objects in the stack and deciphers between * 3 possible situations in order to find out what should be done. These are as follows:
* If the 3 objects are two ValueTokens that in between them is a BinaryOp Token, it pops them all out, calculates the
* Expression and pushes the result back in
* If the 3 objects are two objects of Bracket type with the first object one an opening bracket and the last object a 
* Closing bracket and in between there is a ValueToken it pops them all out and only pushes the ValueToken.
* Otherwise, it ends it's run
* @param token A CalcToken data type representing the token we receive
*/  



	private void collapse(CalcToken token){
		
		boolean endCollapse = false;
			
		
			
			while (!endCollapse && this.stack.size() > 1){
            Object e1 = this.stack.peek(this.stack.size());
				    Object e2 = this.stack.peek(this.stack.size() - 1);
            Object e3 = this.stack.peek(this.stack.size() - 2);
				    double ans = 0;
				    if (e1 instanceof ValueToken && e2 instanceof BinaryOp && e3 instanceof ValueToken && (token instanceof BinaryOp && 
						(((BinaryOp) token).getPrecedence() <=  ((BinaryOp) e2).getPrecedence()) || (! (token instanceof BinaryOp)))){
                    e1 = this.stack.pop();
                    e2 = this.stack.pop();
                    e3 = this.stack.pop();
                    ans = ((BinaryOp)e2).operate(((ValueToken)e3).getValue(),((ValueToken)e1).getValue());
					 					ValueToken res = new ValueToken(ans);
						        this.stack.push(res);
  			
				     }else if (e1 instanceof CloseBracket && e3 instanceof OpenBracket) {
					
                    e1 = this.stack.pop();
					          e2 = this.stack.pop();
					          e3 = this.stack.pop();
					          this.stack.push(e2);
					
				     }else{
                    endCollapse = true;
				          }  	
			      }  	
	    }  	  
}
